package com.shiyu.exam.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shiyu.exam.entity.*;
import com.shiyu.exam.exception.ExamException;
import com.shiyu.exam.mapper.AnswerRecordMapper;
import com.shiyu.exam.mapper.ExamRecordMapper;
import com.shiyu.exam.mapper.QuestionMapper;
import com.shiyu.exam.service.AIService;
import com.shiyu.exam.service.AnswerRecordService;
import com.shiyu.exam.service.ExamRecordService;
import com.shiyu.exam.service.PaperService;
import com.shiyu.exam.vo.ExamRankingVO;
import com.shiyu.exam.vo.StartExamVo;
import com.shiyu.exam.vo.SubmitAnswerVo;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 考试记录Service实现类
 * 实现考试记录相关的业务逻辑
 */
@Service
public class ExamRecordServiceImpl extends ServiceImpl<ExamRecordMapper, ExamRecord> implements ExamRecordService {
    @Autowired
    private PaperService paperService;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private AnswerRecordService answerRecordService;

    @Autowired
    private AnswerRecordMapper answerRecordMapper;

    @Autowired
    private AIService aiService;


    // 开始考试
    @Override
    public ExamRecord startExam(@NotNull StartExamVo startExamVo) {
/*        //如果先要创建考试记录，则需要先查询，
        //看看是否有同名的试卷id，考生的姓名，考试记录的状态是否是正在进行中的
        ExamRecord oldExamRecord = baseMapper.selectOne(new LambdaQueryWrapper<ExamRecord>()
                .eq(ExamRecord::getExamId, startExamVo.getPaperId())
                .eq(ExamRecord::getStudentName, startExamVo.getStudentName())
                .eq(ExamRecord::getStatus, "进行中")
        );
        //如果有，则返回
        if (oldExamRecord != null) {
            *//**
         * 开始考试
         *
         * 幂等逻辑：同一学生、同一套试卷，如果已存在“进行中”的考试记录，
         * 则直接返回该记录，防止因网络重试或用户重复点击导致重复创建。
         * 否则，新建一条“进行中”的考试记录并返回。
         *
         *//*
            return oldExamRecord;
        }
        //如果没有，则创建
        ExamRecord examRecord = new ExamRecord();
        //考试记录的试卷id
        examRecord.setExamId(startExamVo.getPaperId());
        //考试记录的考生姓名
        examRecord.setStudentName(startExamVo.getStudentName());
        //考试记录的状态
        examRecord.setStatus("进行中");
        //考试记录的开始时间
        examRecord.setStartTime(LocalDateTime.now());
        //设置考试中的切屏次数
        examRecord.setWindowSwitches(0);
        //保存考试记录
        baseMapper.insert(examRecord);
        return examRecord;*/
        //-------------------------------------------------------------
        //开始考试，要创建考试记录
        //首先先进行查询，如果这个记录存在了，则返回，如果不存在，则创建新的记录
        ExamRecord examRecordExit = baseMapper.selectOne(new LambdaQueryWrapper<ExamRecord>().eq(ExamRecord::getExamId, startExamVo.getPaperId())
                .eq(ExamRecord::getStudentName, startExamVo.getStudentName()));
        //如果这个记录存在了，则返回这个记录，让其继续答题
        if (examRecordExit != null) {
            return examRecordExit;
        }
        // 如果不存在，则创建
        ExamRecord examRecord = new ExamRecord();
        //先设置保存的试卷记录的ID
        examRecord.setExamId(startExamVo.getPaperId());
        //设置保存的考生姓名
        examRecord.setStudentName(startExamVo.getStudentName());
        //设置保存的考试状态为进行中
        examRecord.setStatus("进行中");
        //设置考试开始的时间
        examRecord.setStartTime(LocalDateTime.now());
        //设置切屏的次数
        examRecord.setWindowSwitches(0);
        //全部设置之后，就可以调用自带的方法进行保存
        baseMapper.insert(examRecord);
        return examRecord;

    }

    // 点击开始考试，并开始回显
    @Override
    public ExamRecord getExamRecordDetailsById(Integer id) {
        /*//根据id查询考试记录详情
        ExamRecord examRecord = baseMapper.selectById(id);
        //获取所有的题目
        Paper paperById = paperService.getPaperById(examRecord.getExamId());
        // 获取所有的题的答案和选项
        // 获取试卷中的所有题目
        List<Question> questions = paperById.getQuestions();
        questions.forEach(question -> {
            //根据题目id包含答案和选项的题目
            Question questionDetailsById = questionMapper.getQuestionDetailsById(question.getId());
            // 设置题目的答案和选项
            question.setAnswer(questionDetailsById.getAnswer());
            question.setChoices(questionDetailsById.getChoices());
        });
        // 对题目进行排序
        questions.sort((o1, o2) -> o1.getType().compareTo(o2.getType()));
        // 设置试卷中的题目
        examRecord.setPaper(paperById);
        //获取考试记录和答题记录
        List<AnswerRecord> answerRecords = answerRecordMapper.selectList(
                new LambdaQueryWrapper<AnswerRecord>()
                        .eq(AnswerRecord::getExamRecordId, id));
        if (answerRecords != null && answerRecords.size() > 0) {
            examRecord.setAnswerRecords(answerRecords);
        }
        return examRecord;
        // TODO:试试使用SQL*/
        //--------------------------------------------------------------------------------------------
        //首先，我点击了开始考试，然后应该根据这个id进行查询，获取考试记录详情
        //首先查询这个id的详细试卷的信息
        //我现在传过来了考试记录的id，根据这个id我可以查到其对应的考试的记录
        ExamRecord examRecord = baseMapper.selectById(id);
        //根据这个考试记录我就可以获取其对应的试卷中的详细信息
        Paper paperById = paperService.getPaperById(examRecord.getExamId());
        //拿到了试卷，就可以获取所有的题目
        List<Question> questions = paperById.getQuestions();
        // 1. 拿到试卷里刚才查出来的 List<Question>，准备挨个补全
        questions.forEach(question -> {

            // 2. 根据当前题目的主键 ID，再去数据库查一次，把 answer、choices 等完整字段捞回来
            //    questionDetailsById 是一个全新的、字段完整的 Question 对象
            Question questionDetailsById = questionMapper.getQuestionDetailsById(question.getId());

            // 3. 把完整对象里的“正确答案”填到原来“空答案”的题目里
            question.setAnswer(questionDetailsById.getAnswer());

            // 4. 把完整对象里的“选项列表”也填到原来的题目里
            question.setChoices(questionDetailsById.getChoices());

        });
        //拿到之后对题目进行排序
        questions.sort(((o1, o2) -> o1.getType().compareTo(o2.getType())));
        /**
         * 排序等价于
         *
         * questions.sort(new Comparator<Question>() {
         *     @Override
         *     public int compare(Question o1, Question o2) {
         *         return o1.getType().compareTo(o2.getType());
         *     }
         * });
         */
        //设置试卷中的题目
        examRecord.setPaper(paperById);
        //获取考试记录和答题记录
        List<AnswerRecord> answerRecords = answerRecordMapper.selectList(
                new LambdaQueryWrapper<AnswerRecord>()
                        .eq(AnswerRecord::getExamRecordId, id)
        );
        //如果现在的answerRecords不为空，则证明有答题记录
        if (answerRecords != null && answerRecords.size() > 0) {
            examRecord.setAnswerRecords(answerRecords);
        }
        return examRecord;
    }

    // 提交答案并且进行批卷
    @Override
    public void submitAnswers(Integer examRecordId, List<SubmitAnswerVo> answers) {
        //根据考试的id进行查询考试记录
        ExamRecord examRecord = baseMapper.selectById(examRecordId);
        //设置考试记录的结束时间
        examRecord.setEndTime(LocalDateTime.now());
        //设置考试记录的状态为已完成
        examRecord.setStatus("已完成");
        // 调用自带的更新的方法
        baseMapper.updateById(examRecord);
        //将List<SubmitAnswerVo> answers转换为List<AnswerRecord>类型
/**
 * 将前端传来的答题卡 List<SubmitAnswerVo> 转换成数据库实体 List<AnswerRecord>，
 * 以便后续一次性批量插入 answer_record 表。
 *
 * 字段说明：
 *   examRecordId  -> 本次考试记录的主键，外层方法参数，需要手动写入每一条 AnswerRecord。
 *   questionId    -> 题目 id，SubmitAnswerVo 里自带。
 *   userAnswer    -> 用户实际填写的答案，SubmitAnswerVo 里自带。
 */
/*        List<AnswerRecord> answerRecordList =
                // ① 调用 Collection 的 stream() 方法，把 List 变成一条“流水线”
                answers.stream()
                        // ② map() 表示“映射”：把流水线上的每一个 SubmitAnswerVo 元素，
                        //    加工成另一个对象 AnswerRecord。
                        .map(answer -> {
                            // ③ 创建数据库实体对象 AnswerRecord。
                            //    这里用了一个全参构造器：new AnswerRecord(examRecordId, questionId, userAnswer)
                            //    如果实体类没有对应构造器，也可以先 new 空对象再 setXxx。
                            AnswerRecord answerRecord =
                                    new AnswerRecord(
                                            examRecordId,          // 考试记录 id（同一次考试所有题目都相同）
                                            answer.getQuestionId(),// 当前这条答案对应的题目 id
                                            answer.getUserAnswer() // 用户提交的答案内容
                                    );
                            // ④ 把造好的实体“放回”流水线，继续往下走
                            return answerRecord;
                        })
                        // ⑤ collect() 是“收集”操作，把流水线末端的结果重新打包成 List
                        //    Collectors.toList() 会返回一个 ArrayList 实例
                        .collect(Collectors.toList());*/
        /**
         * 至此，answerRecordList 里就是可以直接丢给 MyBatis-Plus 批量插入的实体集合，
         * 后续调用 answerRecordService.saveBatch(answerRecordList) 即可一次性写入数据库。
         */
        //   ↓↓
        //   ↓↓
        //   ↓↓
        //   ↓↓
        //将List<SubmitAnswerVo> answers转换为List<AnswerRecord>类型
        List<AnswerRecord> answerRecordList = answers.stream().map(answer -> {
            AnswerRecord answerRecord = new AnswerRecord(examRecordId, answer.getQuestionId(), answer.getUserAnswer());
            return answerRecord;
        }).collect(Collectors.toList());

        //调用批量插入的方法进行批量插入
        answerRecordService.saveBatch(answerRecordList);

        //调用AI批卷的方法进行批卷
        gradePaper(examRecordId);
    }

    // 删除考试记录
    @Override
    public void removeExamRecordById(Integer id) {
        //根据id查询考试记录
        ExamRecord examRecord = baseMapper.selectById(id);
        //判断考试记录的状态
        if ("进行中".equals(examRecord.getStatus())) {
            //不能删除
            throw new ExamException(1010, "不能删除进行中的考试记录");
        }
        //删除考试记录
        baseMapper.deleteById(id);
        //删除答题记录
        answerRecordMapper.delete(
                new LambdaQueryWrapper<AnswerRecord>()
                        .eq(AnswerRecord::getExamRecordId, id));
    }

    // 获取考试排名--> 写SQL
    @Override
    public List<ExamRankingVO> getExamRankingVOList(Integer paperId, Integer limit) {
        return baseMapper.getExamRankingVOList(paperId, limit);
    }


    //============================================================================================
    //AI批卷的方法
    private void gradePaper(Integer examRecordId) {
/*        //取数据
        //ExamRecord → AnswerRecords + Paper + Questions
        //1.根据id进行查询考试记录
        ExamRecord examRecord = getExamRecordDetailsById(examRecordId);
        //获取答题记录
        List<AnswerRecord> answerRecords = examRecord.getAnswerRecords();
        //获取试卷中的所有的题
        List<Question> questions = examRecord.getPaper().getQuestions();
        //建索引
        //List → Map<Long, AnswerRecord>
        Map<Long, Question> questionMap = questions.stream()
                .collect(Collectors
                        .toMap(Question::getId,
                                question -> question));
        answerRecords.forEach(answerRecord -> {
            //获取答题记录中的id
            Long questionId = answerRecord.getQuestionId();
            //根据id进行查询单题
            Question question = questionMap.get(questionId);
            //逐题批改
            //3-a 判断题：把 T/F → TRUE/FALSE
            //3-b 客观题：完全匹配即满分，否则 0 分
            //3-c 简答题：丢给 AI 评分，返回 score + feedback
            //判断是不是判断题
            if ("JUDGE".equals(question.getType())) {
                //如果是选择，则设置T和F为TRUE和FALSE
                answerRecord.setUserAnswer(
                        answerRecord.getUserAnswer()
                                .equalsIgnoreCase("T") ? "TRUE" : "FALSE");
            }
            // 获取问题的正确答案
            String answer = question.getAnswer().getAnswer();
            // 获取用户的提交的答案
            String userAnswer = answerRecord.getUserAnswer();
            //判断是不是非简答
            if (!"TEXT".equalsIgnoreCase(question.getType())) {
                //判断用户提交的答案和正确答案是否一致
                if (answer.equalsIgnoreCase(userAnswer)) {
                    //一致的话就可以设置分数了
                    answerRecord.setScore(question.getScore().intValue());
                    //设置是否是正确的
                    answerRecord.setIsCorrect(1);
                } else {
                    //不一致的话就设置分数为0
                    answerRecord.setScore(0);
                    //设置是否是正确的
                    answerRecord.setIsCorrect(0);
                }
            }
            //调用AI处理简答题
            GradingResult gradingResult = aiService.gradeTextQuestion(question, userAnswer);
            //设置简答题的得分
            answerRecord.setScore(gradingResult.getScore());
            //根据AI的判分结果设置是否正确（打满分才算正确）
            if (gradingResult.getScore() == question.getPaperScore().intValue()) {
                //证明简答题得了满分
                answerRecord.setIsCorrect(1);
                //设置AI的反馈
                answerRecord.setAiCorrection(gradingResult.getFeedback());
            } else if (gradingResult.getScore() == 0) {
                //一分没得
                answerRecord.setIsCorrect(0);
                answerRecord.setAiCorrection(gradingResult.getReason());
            } else {
                //部分得分
                answerRecord.setIsCorrect(2);
                answerRecord.setAiCorrection(gradingResult.getReason());
            }
            //更新答题记录
            answerRecordMapper.updateById(answerRecord);
        });
        //计算总共的得分
        Integer totalScore = answerRecords.stream().mapToInt(AnswerRecord::getScore).sum();
        //计算总共的答题的正确数量
        Integer correctCount = answerRecords.stream().filter(answerRecord -> answerRecord.getIsCorrect() == 1).mapToInt(AnswerRecord::getScore).sum();
        // 将总得分设置到考试的记录中
        examRecord.setScore(totalScore);
        //调用AI获取评语
        String summary = aiService.getSummary(
                totalScore, examRecord.getPaper().getTotalScore().intValue(),
                examRecord.getPaper().getQuestionCount(),
                correctCount
        );
        //将本次考试的总评语设置到考试记录中
        examRecord.setAnswers(summary);
        //设置考试状态为已批阅
        examRecord.setStatus("已批阅");
        //更新考试记录
        baseMapper.updateById(examRecord);*/

        //------------------------------------------------------------------------------------
        /** 1. 取数据：考试记录 + 答题记录 + 试卷 + 题目列表 */
        //首先传进来了examRecordId，我可以根据考试记录的id进行查询
        //查出考试的记录
        //
        ExamRecord examRecord = getExamRecordDetailsById(examRecordId);
        //获取考试记录中的答题记录
        List<AnswerRecord> answerRecords = examRecord.getAnswerRecords();
        //获取试卷中的所有题
        List<Question> questions = examRecord.getPaper().getQuestions();
        /** 2. 建索引：Question.id → Question，方便 O(1) 查找 */
        //把列表转换成Map
        Map<Long, Question> questionMap = questions.stream().collect(Collectors.toMap(Question::getId, question -> question));
        /** 3. 逐条批改 */
        answerRecords.forEach(answerRecord -> {
            //获取答题记录中的id
            Long questionId = answerRecord.getQuestionId();
            //根据id进行查询单题
            Question question = questionMap.get(questionId);
            /** 4. 判断题格式统一 */
            //判断是不是判断题
            if ("JUDGE".equals(question.getType())) {
                //如果是判断，则设置T和F为TRUE和FALSE
                answerRecord.setUserAnswer(answerRecord.getUserAnswer().equalsIgnoreCase("T") ? "TRUE" : "FALSE");
            }
            // 获取问题的正确答案
            String answer = question.getAnswer().getAnswer();
            // 获取用户的提交的答案
            String userAnswer = answerRecord.getUserAnswer();
            /** 5. 客观题：完全匹配给满分，否则 0 分 */
            //判断是不是非简答题
            //如果不是简答的话，我们可以自行进行比对
            if (!"TEXT".equalsIgnoreCase(question.getType())) {
                //如果一致，则正确
                if (answer.equalsIgnoreCase(userAnswer)) {
                    //设置正确的得分
                    answerRecord.setScore(question.getScore());
                    //设置正确的标志
                    answerRecord.setIsCorrect(1);
                } else {
                    //否则，错误
                    answerRecord.setScore(0);
                    //设置错误的标志
                    answerRecord.setIsCorrect(0);
                }
            }
            /** 6. 简答题：调用 AI 批改 */
            //调用AI处理简答题
            GradingResult gradingResult = aiService.gradeTextQuestion(question, userAnswer);
            //设置简答题的得分
            answerRecord.setScore(gradingResult.getScore());
            //根据AI的判分结果设置是否正确（打满分才算正确）
            if (gradingResult.getScore() == question.getPaperScore().intValue()){
                //证明简答题得了满分
                answerRecord.setIsCorrect(1);
                //设置AI的反馈
                answerRecord.setAiCorrection(gradingResult.getFeedback());
            } else if (gradingResult.getScore() == 0) {
                answerRecord.setIsCorrect(0);
                answerRecord.setAiCorrection(gradingResult.getReason());
            }else
            {
                answerRecord.setIsCorrect(2);
                answerRecord.setAiCorrection(gradingResult.getReason());
            }
            //更新答题记录
            answerRecordMapper.updateById(answerRecord);
        });
        /** 7. 计算总得分 */
        //获取总得分并设置进去
        Integer sum = answerRecords.stream().mapToInt(AnswerRecord::getScore).sum();
        examRecord.setScore(sum);
        //获取正确的数量之和
        Integer totalCorrectCount = answerRecords
                .stream()
                .filter(answerRecord -> answerRecord.getIsCorrect() == 1)
                .mapToInt(answerRecord -> 1).sum();
        //获取AI 的评语
        String summary = aiService.getSummary(sum, examRecord.getPaper().getTotalScore().intValue(), examRecord.getPaper().getQuestionCount(), totalCorrectCount);
        //设置评语
        examRecord.setAnswers(summary);
        //设置考试状态为已批阅
        examRecord.setStatus("已批阅");
        //更新考试记录
        baseMapper.updateById(examRecord);
    }
}

